#ifndef VL_NANOTHREADS_HPP
#define VL_NANOTHREADS_HPP

#include "list.hpp"
#include "macros.hpp"
#include "shared_ptr.hpp"

namespace vl
{

struct signal : shared
{
	signal()
	//: _sig_next(0)
	{
	}
	
	virtual ~signal()
	{
	}
	
	//signal* _sig_next;
};

struct light_thread
{
	enum return_status
	{
		ok,
		pause
	};
	
	light_thread()
	: _th_state(0)
	{
	}
	
	virtual return_status run() = 0;
	
	virtual ~light_thread()
	{

	}
	
	int _th_state;
};

struct thread : light_thread
{
	thread()
	: light_thread(), _th_child(0)
	, _th_first_signal(0)
	{
	}

	virtual ~thread()
	{
		delete _th_child;
		
		//delete _th_first_signal;
		/*
		signal* sig = _th_first_signal;
		
		while(sig)
		{
			signal* cur = sig;
			sig = sig->_sig_next;
			
			delete cur;
		}*/
	}
	/*
	signal* _th_next_signal()
	{
		signal* next = _th_first_signal;
		if(next)
			_th_first_signal = next->_sig_next;
		return next;
	}
	*/
	
	template<typename T>
	bool _th_find_signal()
	{
		if(_th_first_signal && dynamic_cast<T const*>(&*_th_first_signal) != 0)
			return true;
	/*
		signal* sigi = _th_first_signal;
		signal** prev_sigi = &_th_first_signal;
		
		for(; sigi; sigi = sigi->_sig_next)
		{
			if(dynamic_cast<T const*>(sigi) != 0)
			{
				// Move to front
				*prev_sigi = sigi->_sig_next;
				_th_enqueue(sigi);
				return true;
			}
			
			prev_sigi = &sigi->_sig_next;
		}*/
	
		return false;
	}
	
	//template<typename T>
	return_status run_with_signal(vl::shared_ptr<signal> sig)
	{
		_th_enqueue(sig);
		return run();
	}
	
	void _th_enqueue(vl::shared_ptr<signal> sig)
	{
		//sig->_sig_next = _th_first_signal;
		//delete _th_first_signal;
		_th_first_signal = sig;
	}
	
	void _th_pop_signal()
	{
	/*
		signal* next = _th_first_signal->_sig_next;
		delete _th_first_signal;
		_th_first_signal = next;*/
		_th_first_signal.reset();
	}

	thread* _th_child;
	shared_ptr<signal> _th_first_signal;
};

#define VLNT(name, params) struct name : public vl::thread { \
name params

#define VLNT_RUN_DECL() return_status run()

#define VLNT_END() return_status run(); };

#define VLNT_DEFINE(name) vl::thread::return_status name::run() { \
switch(_th_state) { case 0:

#define VLNT_DEFINE_END() } return vl::thread::ok; }

#define VLNT_INLINE() vl::thread::return_status run() { \
switch(_th_state) { case 0:

#define VLNT_INLINE_END() } return vl::thread::ok; }

#define VLNT_PAUSE() do { _th_state = __LINE__; VLNT_RETURN_EX(vl::thread::pause); case __LINE__:; \
} while(0)

#define VLNT_CALL(threadtype, params) do { \
_th_child = new threadtype params; \
case __LINE__: \
if(static_cast<threadtype*>(_th_child)->run() == vl::thread::pause) \
{ _th_state = __LINE__;	VLNT_RETURN_EX(vl::thread::pause); } \
delete _th_child; _th_child = 0; } while(0)

#define VLNT_CALL_VALUE(value) do { \
_th_child = value; \
case __LINE__: \
if(_th_child->run() == vl::thread::pause) \
{ _th_state = __LINE__;	VLNT_RETURN_EX(vl::thread::pause); } \
delete _th_child; _th_child = 0; } while(0)

#define VLNT_CALL_VALUE_KEEP(value) do { \
_th_child = value; \
case __LINE__: \
if(_th_child->run() == vl::thread::pause) \
{ _th_state = __LINE__;	VLNT_RETURN_EX(vl::thread::pause); } \
_th_child = 0; } while(0)

#define VLNT_WAIT_UNTIL(c) do { case __LINE__: \
if(!(c)) { _th_state = __LINE__; VLNT_RETURN_EX(vl::thread::pause); } } while(0)

#define VLNT_WAIT_WHILE(c) do { case __LINE__: \
if(c) { _th_state = __LINE__; VLNT_RETURN_EX(vl::thread::pause); } } while(0)

#define VLNT_CUR_SIG(sig) (dynamic_cast<sig const*>(&*_th_first_signal) != 0)
//#define VLNT_WAIT_SIGNAL(sig) VLNT_WAIT_UNTIL(VLNT_SIGNAL(sig))

#define VLNT_SIG(sig) (_th_find_signal<sig>())

/*
#define VLNT_WAIT_SIGNAL(sig) do { \
case __LINE__: _th_state = __LINE__; \
for(vl::signal *_sigi = _th_first_signal, **_prev_sigi = &_th_first_signal; ; _sigi = _sigi->_sig_next) { \
	if(!_sigi) { \
		VLNT_RETURN_EX(vl::thread::pause); } \
	if(dynamic_cast<sig const*>(_sigi) != 0) { \
		*_prev_sigi = _sigi->_sig_next; \
		_th_enqueue(_sigi); \
		break; } \
	_prev_sigi = &_sigi->_sig_next; } } while(0)*/
	
#define VLNT_WAIT_SIGNAL(sig) VLNT_WAIT_UNTIL(VLNT_SIG(sig))

// NOTE! This is only legal to call if VLNT_CUR_SIG(sig) is true!
#define VLNT_SIGDATA(sig) (static_cast<sig const&>(*_th_first_signal))

#define VLNT_SIGDONE() _th_pop_signal()

#define VLNT_RETURN() VLNT_RETURN_EX(vl::thread::ok)

#define VLNT_RETURN_EX(type) return type

// ---- EXPERIMENTAL ----
#define VLNT_EXP_DECL0(name) vl::thread* name()
#define VLNT_EXP_DECL1(name, type) vl::thread* name(type)

#define VLNT_EXP_DEFRUN(name) \
vl::thread::return_status th_##name::run(vl::signal const* _sig) { \
switch(_th_state) { case 0: \

#define VLNT_EXP_DEFRUN_END() \
} return vl::thread::ok; }

#define VLNT_EXP_DEF0(name) \
VLNT_EXP_DEF_CLASS(name, ()) \
VLNT_EXP_DEF_CLASS_END() \
vl::thread* name() { return new th_##name; } \
VLNT_EXP_DEFRUN(name)

#define VLNT_EXP_DEF_END() VLNT_EXP_DEFRUN_END()

#define VLNT_EXP_DEF_CLASS(name, params) \
struct th_##name : public vl::thread { \
th_##name params

#define VLNT_EXP_DEF_CLASS_END() };

/*
struct th_foobar : vl::thread
{
	th_foobar(int i)
	// <CUSTOM>
	{
	}
	
	// </CUSTOM>
};

vl::thread* foobar(int i)
{
	return new th_foobar(i);
}
*/

#ifndef VLNT_SUPPRESS_NICE_NAMES
#define NT_Thread VLNT
#define NT_RunDecl VLNT_RUN_DECL
#define NT_ThreadEnd VLNT_END
#define NT_Define VLNT_DEFINE
#define NT_DefineEnd VLNT_DEFINE_END
#define NT_Inline VLNT_INLINE
#define NT_InlineEnd VLNT_INLINE_END
#define NT_Pause VLNT_PAUSE
#define NT_Call VLNT_CALL
#define NT_CallValue VLNT_CALL_VALUE
#define NT_CallValueKeep VLNT_CALL_VALUE_KEEP
#define NT_WaitUntil VLNT_WAIT_UNTIL
#define NT_WaitWhile VLNT_WAIT_WHILE
#define NT_CurSig VLNT_CUR_SIG
#define NT_Sig VLNT_SIG
#define NT_WaitSignal VLNT_WAIT_SIGNAL
#define NT_SigData VLNT_SIGDATA
#define NT_SigDone VLNT_SIGDONE
#define NT_Return VLNT_RETURN
#endif // VLNT_NICE_NAMES

/*
struct thread_group
{
	void add(thread* th)
	{
		threads.push_back(th);
	}
	
	void slice()
	{
		FOREACH_DELETE(list<thread>, i, threads)
		{
			if(i->run() != thread::pause)
			{
				threads.erase(i);
			}
		}
	}
	
	list<thread> threads;
};*/

}

#endif //VL_NANOTHREADS_HPP